module(..., package.seeall)
local html_forms = require("zwsapi.html_forms")
local util = require("zwsapi.util")
local rootact=require("zwsapi.actions.util")
CONFIRMATION_FORM_SPEC = [[
   new_password = {1.31, "password"}
]]

actions = {}

-----------------------------------------------------------------------------
-- Displays the registration form.
-----------------------------------------------------------------------------
function actions.show_form(user,dal,node,act,request)
	-- prepare the timestamp and token
	local post_timestamp = os.time()
	local post_token = dal.auth:Timestamp_token(post_timestamp)

	local email_field = ""
	if dal.config.REQUIRE_EMAIL_ACTIVATION then
		email_field = [[new_email = {1.33, "text_field"} ]]
	end

	-- add the terms of service acceptance checkbox if configured
	local tos_field = ""
	if dal.config.TERMS_OF_SERVICE_NODE then
		local tos_template = node.translator.translate_key("I_AGREE_TO_TERMS_OF_SERVICE")
		local text = cosmo.fill(tos_template, {
			url = dal.config.TERMS_OF_SERVICE_NODE,
		})
		tos_field = [[agree_tos = {1.34, "checkbox_text", text="]] .. text .. [["}]]
	end

	-- prepare the edit form
	local html_for_fields, field_list = html_forms.make_html_form{
		field_spec =[[
						new_username = {1.30, "text_field", div_class="autofocus"}
						new_password = {1.31, "password"}
						new_password_confirm = {1.32, "password"}
					]] .. email_field .. tos_field,
		values={
				new_username = request.params.new_username or "",
				new_password = request.params.new_password or "",
				new_password_confirm = request.params.new_password_confirm or "",
				new_email = request.params.new_email or "",
				},
		templates  = node.templates, 
		translator = node.translator,
		hash_fn=function(field_name)
					return dal:hash_field_name(field_name, post_token)
				end
	}

	
	local captcha_html = ""
	if dal.captcha then
		for _, field in ipairs(dal.captcha:get_fields()) do
			table.insert(field_list, field)
		end
		captcha_html = dal.captcha:get_html()
	end

	node.inner_html = cosmo.f(node.templates.REGISTRATION){
		html_for_fields = html_for_fields,
		node_name		 = request.node_name,
		post_fields	  = table.concat(field_list,","),
		post_token		= post_token,
		post_timestamp  = post_timestamp,
		action_url		= dal:make_url(node.__id),
		action			 = "submit",
		submitLabel	  = node.translator.translate_key("SUBMIT"),
		captcha			= captcha_html,
	}
	return node.wrappers.default(user,dal,node,act,request)
end


function create_email_activation_ticket(args)
	local dal = args.dal 
	local node = args.node
	assert(node)
	-- Create the activation ticket
	local uid = md5.sumhexa(args.username .. dal:get_uid("register") .. os.time())
	local ticket_id = ("activate/%s"):format(uid)
	local ticket = dal:GetNode(ticket_id)
	ticket:__update_fields{
				prototype = "dal/@Account_Activation_Ticket",
				username  = args.username,
				email	  = args.email,
				hash		= args.hash,
				numtries  = "0"
			}
	ticket:__save()

	-- Email the user
	local link = "http://" .. dal.config.DOMAIN .. dal:make_url(ticket_id)
	local status, err = dal:sendmail{
		from	 = dal.config.CONFIRMATION_ADDRESS_FROM,
		to		= args.email,
		subject = node.translator.translate_key("ACCOUNT_ACTIVATION"),
		body	 = cosmo.f(node.translator.translate_key("ACTIVATION_MESSAGE_BODY")){
						 site_name=dal.config.DOMAIN,
						 activation_link=link, 
					 }
	}
	return status==1, err
end

-----------------------------------------------------------------------------
-- Creates a new account and possible a node for user's profile
-----------------------------------------------------------------------------
function create_new_account(user,dal,node,act,request, username, password, metadata)
	lcased_username = username:lower()
	dal.auth:add_user(dal,lcased_username, password, metadata)
	node:post_translated_success("SUCCESSFULLY_CREATED_ACCOUNT")
	-- If needed, create a user node
	if dal.config.USE_USER_NODES then
		local unid = (dal.config.USER_NODE_PREFIX or "@ws/people/")..lcased_username
		local user_node = dal:SureNode(unid)
		if not user_node then
			user_node=dal:MakeNode(unid,'',nil,nil,true)
		end
		user_node:__update_fields{
					_pto = dal.config.USER_NODE_PROTOTYPE or "@ws/@userprofile",
					title = username,
					creation_time = os.time(),
				}
		local ok, err = pcall(user_node.__save,user_node)
		if ok then
			node:post_translated_success("SUCCESSFULLY_CREATED_USER_NODE")
		else
			node:post_translated_error("COULD_NOT_CREATE_USER_NODE", err)
		end
	end
end
-----------------------------------------------------------------------------
-- Handles the submission of the registration form.
-----------------------------------------------------------------------------
function actions.submit(user,dal,node,act,request)
	function err_msg(err_code)
		request.try_again = true
		node:post_error(node.translator.translate_key(err_code))
	end
	
	local post_ok, err = rootact.check_post_parameters(user,dal,node,act,request)
	if not post_ok then
		err_msg(err)
	else 
		local p = request.params
		-- the form is legit, let's check that username and password are ok
		for message, test in pairs(dal.config.USERNAME_RULES or {}) do
			if not test(request.params.new_username) then 
				request.try_again = true
				node:post_error(message)
			end
		end
		for message, test in pairs(dal.config.PASSWORD_RULES or {}) do
			if not test(request.params.new_password) then 
				request.try_again = true
				node:post_error(message)
			end
		end

		-- check confirmation password
		if p.new_password ~= p.new_password_confirm then 
			err_msg("TWO_VERSIONS_OF_NEW_PASSWORD_DO_NOT_MATCH")
		end

		if dal.REQUIRE_EMAIL_CONFIRMATION 
			and not p.new_email:match("^%S+@%S+$") then 
				err_msg("NEW_EMAIL_NOT_VALID")
		end

		-- check that the user name is not taken
		if  dal.auth:user_exists(dal,p.new_username) then
			err_msg("USERNAME_TAKEN")
		end

		-- optionally check for TOS acceptance
		if dal.config.TERMS_OF_SERVICE_NODE
				  and not p.agree_tos then
			err_msg("MUST_CONFIRM_TOS")
		end

		-- test captcha, if configured
		if dal.captcha then
			local captcha_ok, err = dal.captcha:verify(request.POST, request.wsapi_env.REMOTE_ADDR)
			if not captcha_ok then
				err_msg("COULD_NOT_VERIFY_CAPTCHA", err)
			end
		end
	end

	if request.try_again then
		return zdc.nodeapi.getnodeact(dal,node,'show')(user,dal,node,act,request)
	end

	if dal.config.REQUIRE_EMAIL_ACTIVATION then
		local ok, err = create_email_activation_ticket{
				username  = request.params.new_username,
				email	  = request.params.new_email,
				hash		= md5.sumhexa(request.params.new_password),
				dal	= dal,
				node		= node,
		}
		if ok then
			node:post_success(node.translator.translate_key("ACTIVATION_MESSAGE_SENT"))
		else
			node:post_error(node.translator.translate_key("ERROR_SENDING_ACTIVATION_EMAIL").." ("..err..")")
		end
	else
		create_new_account(user,dal,node,act,request,request.params.new_username, request.params.new_password)
		request.user, request.auth_token = dal.auth:Authenticate(dal,request.params.new_username, request.params.new_password)	
	end

	node.inner_html = ""
	return node.wrappers.default(user,dal,node,act,request)
end

-----------------------------------------------------------------------------
-- Displays the activation form.
-----------------------------------------------------------------------------
function actions.confirm(user,dal,node,act,request)
	local fields = {}
	fields.new_password = request.params.new_password or ""

	local post_timestamp = os.time()
	local post_token = dal.auth:Timestamp_token(post_timestamp)

	local html_for_fields, field_list = html_forms.make_html_form{
		field_spec = CONFIRMATION_FORM_SPEC,
		templates  = node.templates, 
		translator = node.translator,
		values	  = fields,
		hash_fn	 = function(field_name)
			return dal:hash_field_name(field_name, post_token)
		end
	}

	table.insert(field_list, "recaptcha_challenge_field")
	table.insert(field_list, "recaptcha_response_field")

	node.inner_html = cosmo.f(node.templates.REGISTRATION){
		html_for_fields = html_for_fields,
		node_name		 = node.__id,
		post_fields	  = table.concat(field_list,","),
		post_token		= post_token,
		post_timestamp  = post_timestamp,
		submitLabel	  = node.translator.translate_key("CONFIRM"),
		action_url		= dal:make_url(node.name),
		action			 = "activate",
		captcha			= "",
	}

	node:post_notice(node.translator.translate_key("PLEASE_CONFIRM_PASSWORD"))
	return node.wrappers.default(user,dal,node,act,request)
end

-----------------------------------------------------------------------------
-- Handles submitted activation form.
-----------------------------------------------------------------------------
function actions.activate(user,dal,node,act,request)
	function err_msg(err_code)
		request.try_again = true
		node:post_error(node.translator.translate_key(err_code))
	end

	local password = request.params.new_password or ""
	local hash = md5.sumhexa(password)
	--local confirm, numtries, email, username = util.split(node.content, "\n")

	if node.hash ~= hash then -- wrong password
		local numtries = tonumber(node.numtries or "0") or 0
		if numtries < 3 then
			numtries = numtries + 1

			node:__update_fields({numtries = numtries + 1})

			err_msg("COULD_NOT_CONFIRM_NEW_PASSWORD")
			node = dal:save_node(node, request, "dal",
				"Invalid confirmation attempt")
			return actions.confirm(node, request, dal)
		else
			err_msg("INVALID_ACTIVATION_TICKET")
			request.params.new_username = nil
			node.inner_html = nil
			return node.wrappers.default(user,dal,node,act,request)
		end
	else
		-- Verify first that the account still no longer exists
		if dal.auth:user_exists(dal,node.username) then
			err_msg("USERNAME_TAKEN")
			return actions.show_form(user,dal,node,act,request)
		else
			create_new_account(user,dal,node,act,request,node.username, password, {email = node.email})
			request.user, request.auth_token = dal.auth:Authenticate(dal,node.username, password)	
		end
	end

	node.inner_html = ""
	return node.wrappers.default(user,dal,node,act,request)
end

